/**
 * 
 */
package map;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author balage
 *
 */
public class NodeDataBase {

	private final RandomAccessFile file;
	
	private final Map cache = new HashMap();
	
	private long cachesize = 0;
	
	private final long nodenum;
	
	public static int slotsize = 1+4*8;
	
	private void remove(Long ID){
		cache.remove(ID);
		cachesize--;
	}
	
	private void cleancache(long id){
		if (cachesize > 200){
			Long ID = new Long(id);
			NodeSlot ns = (NodeSlot)cache.get(ID);
			if (ns != null){
				switch(ns.type){
				case NodeSlot.TYPE_NODE:
				case NodeSlot.TYPE_WAY:
					if (ns.w != Long.MAX_VALUE)
						cleancache(ns.w);
					if (cachesize > 200)
						remove(ID);
				break;
				case NodeSlot.TYPE_LIST:
					if (ns.id != Long.MAX_VALUE)
						cleancache(ns.id);
				case NodeSlot.TYPE_SPLIT_LAT:
				case NodeSlot.TYPE_SPLIT_LON:
					if (ns.l1 != Long.MAX_VALUE)
						cleancache(ns.l1);
					if (ns.l2 != Long.MAX_VALUE)
						cleancache(ns.l2);
					if (ns.w != Long.MAX_VALUE)
						cleancache(ns.w);
					if (cachesize > 200)
						remove(ID);
				break;
				}
			}
		}
	}
	
	private NodeSlot getSlot(long id) throws IOException{
		Long ID = new Long(id);
		NodeSlot ns = (NodeSlot)cache.get(ID);
		if (ns != null){
			return ns;
		}
		if (id >= nodenum){
			System.err.println("Tried of access node "+id+" but there are only "+nodenum);
			return null;
		}	
		file.seek(id*slotsize);
		ns = new NodeSlot();
		ns.type = file.readByte();
		ns.id = file.readLong();
		ns.l1 = file.readLong();
		ns.l2 = file.readLong();
		ns.w = file.readLong();
		ns.poz = id;
		cache.put(ID, ns);
		cachesize++;
		return ns;
	}
	
	public NodeDataBase(File file) throws IOException {
		this.file = new RandomAccessFile(file, "r");
		System.out.println("Map file size: "+this.file.length());
		nodenum = this.file.length()/slotsize;
	}
	
	private boolean readNodes(List nodes,NodeSlot node,int level, double minlat, double minlon, double maxlat, double maxlon) throws IOException{
		//System.out.println("node: "+node);
		if (node == null)
			return false;
		switch(node.type){
		case NodeSlot.TYPE_NODE:
			if (node.w != Long.MAX_VALUE){
				if (readNodes(nodes, getSlot(node.w), level, minlat, minlon, maxlat, maxlon)){
					nodes.add(node);
				}
			}
			break;
		case NodeSlot.TYPE_WAY:
			int nlvl = (int)node.l1%NodeSlot.WAYTYPE_SEPARATOR;
			boolean r = false;
			if (nlvl != 0 && nlvl <= level){
				nodes.add(node);
				r = true;
			}
			if (node.w != Long.MAX_VALUE)
				r = r || readNodes(nodes, getSlot(node.w), level, minlat, minlon, maxlat, maxlon);
			return r;
			//break;
		case NodeSlot.TYPE_LIST:
			if (node.id != Long.MAX_VALUE)
				readNodes(nodes, getSlot(node.id),level, minlat, minlon, maxlat, maxlon);
			if (node.l1 != Long.MAX_VALUE)
				readNodes(nodes, getSlot(node.l1),level, minlat, minlon, maxlat, maxlon);
			if (node.l2 != Long.MAX_VALUE)
				readNodes(nodes, getSlot(node.l2),level, minlat, minlon, maxlat, maxlon);
			if (node.w != Long.MAX_VALUE)
				readNodes(nodes, getSlot(node.w),level, minlat, minlon, maxlat, maxlon);
			break;
		case NodeSlot.TYPE_SPLIT_LAT:
			double lat = Double.longBitsToDouble(node.id);
			if (minlat < lat){
				readNodes(nodes, getSlot(node.l1),level, minlat, minlon, maxlat, maxlon);
			}else{
				cleancache(node.l1);
			}
			if (maxlat > lat){
				readNodes(nodes, getSlot(node.l2),level, minlat, minlon, maxlat, maxlon);
			}else{
				cleancache(node.l2);
			}
			break;
		case NodeSlot.TYPE_SPLIT_LON:
			double lon = Double.longBitsToDouble(node.id);
			if (minlon < lon){
				readNodes(nodes, getSlot(node.l1),level, minlat, minlon, maxlat, maxlon);
			}else{
				cleancache(node.l1);
			}
			if (maxlon > lon){
				readNodes(nodes, getSlot(node.l2),level, minlat, minlon, maxlat, maxlon);
			}else{
				cleancache(node.l2);
			}
			break;
		}
		return false;
	}
	
	public List getNodes(int level, double minlat, double minlon, double maxlat, double maxlon) throws IOException{
		List result = new ArrayList();
		
//		double mlat = Math.ceil(minlat*100)/100;
//		double mlon = Math.ceil(minlon*100)/100;
//		
//		for(double lat = mlat;lat<=maxlat;lat+=0.01)
//			for(double lon = mlon;lon<maxlon;lon+=0.01){
//				NodeSlot ns = new NodeSlot();
//				ns.l1 = Double.doubleToLongBits(lat);
//				ns.l2 = Double.doubleToLongBits(lon);
//				result.add(ns);
//			}
		readNodes(result, getSlot(0),level, minlat, minlon, maxlat, maxlon);
		
		return result;
	}
	
	
}
